home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Text / Show / Less / less-252 / edit.c < prev    next >
C/C++ Source or Header  |  1994-10-15  |  12KB  |  639 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. #include "less.h"
  29.  
  30. #if HAVE_FCNTL_H
  31. #include <fcntl.h>
  32. #endif
  33.  
  34. public int fd0 = 0;
  35. #define    ISPIPE(fd)    ((fd)==fd0)
  36.  
  37. extern int ispipe;
  38. extern int new_file;
  39. extern int errmsgs;
  40. extern int quit_at_eof;
  41. extern int file;
  42. extern int cbufs;
  43. extern char *every_first_cmd;
  44. extern int any_display;
  45. extern int force_open;
  46. extern int is_tty;
  47. extern IFILE curr_ifile;
  48. extern IFILE old_ifile;
  49. extern struct scrpos initial_scrpos;
  50.  
  51. #if LOGFILE
  52. extern int logfile;
  53. extern int force_logfile;
  54. extern char *namelogfile;
  55. #endif
  56.  
  57. static char *curr_altfilename = NULL;
  58.  
  59.  
  60. /*
  61.  * Textlist functions deal with a list of words separated by spaces.
  62.  * init_textlist sets up a textlist structure.
  63.  * forw_textlist uses that structure to iterate thru the list of
  64.  * words, returning each one as a standard null-terminated string.
  65.  * back_textlist does the same, but runs thru the list backwards.
  66.  */
  67.     public void
  68. init_textlist(tlist, str)
  69.     struct textlist *tlist;
  70.     char *str;
  71. {
  72.     char *s;
  73.     
  74.     tlist->string = skipsp(str);
  75.     tlist->endstring = tlist->string + strlen(tlist->string);
  76.     for (s = str;  s < tlist->endstring;  s++)
  77.     {
  78.         if (*s == ' ')
  79.             *s = '\0';
  80.     }
  81. }
  82.  
  83.     public char *
  84. forw_textlist(tlist, prev)
  85.     struct textlist *tlist;
  86.     char *prev;
  87. {
  88.     char *s;
  89.     
  90.     /*
  91.      * prev == NULL means return the first word in the list.
  92.      * Otherwise, return the word after "prev".
  93.      */
  94.     if (prev == NULL)
  95.         s = tlist->string;
  96.     else
  97.         s = prev + strlen(prev);
  98.     if (s >= tlist->endstring)
  99.         return (NULL);
  100.     while (*s == '\0')
  101.         s++;
  102.     if (s >= tlist->endstring)
  103.         return (NULL);
  104.     return (s);
  105. }
  106.  
  107.     public char *
  108. back_textlist(tlist, prev)
  109.     struct textlist *tlist;
  110.     char *prev;
  111. {
  112.     char *s;
  113.     
  114.     /*
  115.      * prev == NULL means return the last word in the list.
  116.      * Otherwise, return the word before "prev".
  117.      */
  118.     if (prev == NULL)
  119.         s = tlist->endstring;
  120.     else if (prev <= tlist->string)
  121.         return (NULL);
  122.     else
  123.         s = prev - 1;
  124.     while (*s == '\0')
  125.         s--;
  126.     if (s <= tlist->string)
  127.         return (NULL);
  128.     while (s[-1] != '\0' && s > tlist->string)
  129.         s--;
  130.     return (s);
  131. }
  132.  
  133.  
  134.     static void
  135. close_file()
  136. {
  137.     struct scrpos scrpos;
  138.     
  139.     if (curr_ifile == NULL_IFILE)
  140.         return;
  141.     /*
  142.      * Save the current position so that we can return to
  143.      * the same position if we edit this file again.
  144.      */
  145.     get_scrpos(&scrpos);
  146.     if (scrpos.pos != NULL_POSITION)
  147.     {
  148.         store_pos(curr_ifile, &scrpos);
  149.         lastmark();
  150.     }
  151.     /*
  152.      * Close the current file, unless it is a pipe.
  153.      */
  154.     if (!ISPIPE(file))
  155.     {
  156.         close(file);
  157.         file = -1;
  158.     }
  159.     /*
  160.      * If we opened a file using an alternate name,
  161.      * do special stuff to close it.
  162.      */
  163.     if (curr_altfilename != NULL)
  164.     {
  165.         close_altfile(curr_altfilename, get_filename(curr_ifile));
  166.         free(curr_altfilename);
  167.         curr_altfilename = NULL;
  168.     }
  169.     curr_ifile = NULL_IFILE;
  170. }
  171.  
  172. /*
  173.  * Edit a new file.
  174.  * Filename == "-" means standard input.
  175.  * Filename == NULL means just close the current file.
  176.  */
  177.     public int
  178. edit(filename)
  179.     char *filename;
  180. {
  181.     if (filename == NULL)
  182.         return (edit_ifile(NULL_IFILE));
  183.     return (edit_ifile(get_ifile(filename, curr_ifile)));
  184. }
  185.     
  186.     public int
  187. edit_ifile(ifile)
  188.     IFILE ifile;
  189. {
  190.     int f;
  191.     int answer;
  192.     int no_display;
  193.     char *filename;
  194.     char *open_filename;
  195.     char *alt_filename;
  196.     PARG parg;
  197.         
  198.     if (ifile == NULL_IFILE)
  199.     {
  200.         /*
  201.          * Close the current file, but don't open a new one.
  202.          */
  203. #if LOGFILE
  204.         end_logfile();
  205. #endif
  206.         close_file();
  207.         return (0);
  208.     }
  209.     if (ifile == curr_ifile)
  210.     {
  211.         /*
  212.          * Already have the correct file open.
  213.          */
  214.         return (0);
  215.     }
  216.     filename = get_filename(ifile);
  217.     alt_filename = open_altfile(filename);
  218.     open_filename = (alt_filename != NULL) ? alt_filename : filename;
  219.     
  220.     if (strcmp(open_filename, "-") == 0)
  221.     {
  222.         /* 
  223.          * Use standard input.
  224.          */
  225.         f = fd0;
  226.     } else if ((parg.p_string = bad_file(open_filename)) != NULL)
  227.     {
  228.         error("%s", &parg);
  229.         free(parg.p_string);
  230.         err1:
  231.         if (alt_filename != NULL)
  232.         {
  233.             close_altfile(alt_filename, filename);
  234.             free(alt_filename);
  235.         }
  236.         del_ifile(ifile);
  237.         return (1);
  238.     }
  239. #if MSOFTC
  240.     else if ((f = open(open_filename, O_RDONLY|O_BINARY)) < 0)
  241. #else
  242.     else if ((f = open(open_filename, 0)) < 0)
  243. #endif
  244.     {
  245.         parg.p_string = errno_message(filename);
  246.         error("%s", &parg);
  247.         free(parg.p_string);
  248.             goto err1;
  249.     } else if (!force_open && !opened(ifile) && bin_file(f))
  250.     {
  251.         parg.p_string = filename;
  252.         answer = query("\"%s\" may be a binary file.  See it anyway? ",
  253.             &parg);
  254.         if (answer != 'y' && answer != 'Y')
  255.         {
  256.             close(f);
  257.             goto err1;
  258.         }
  259.     }
  260.  
  261.     ispipe = ISPIPE(f);
  262. #if LOGFILE
  263. {
  264.     char *s;
  265.     
  266.     /*
  267.      *
  268.      */
  269.     s = namelogfile;
  270.     end_logfile();
  271.     if (f >= 0 && ispipe && s != NULL && is_tty)
  272.         use_logfile(s);
  273. }
  274. #endif
  275.  
  276.     /*
  277.      * We are now committed to using the new file.
  278.      * Close the current input file and set up to use the new one.
  279.      */
  280.     if (curr_ifile != NULL_IFILE)
  281.     {
  282.         old_ifile = curr_ifile;
  283.         close_file();
  284.     }
  285.     /*
  286.      * Get the new ifile.
  287.      * Get the saved position for the file.
  288.      */
  289.     curr_ifile = ifile;
  290.     file = f;
  291.     curr_altfilename = alt_filename;
  292.     set_open(curr_ifile); /* File has been opened */
  293.     get_pos(curr_ifile, &initial_scrpos);
  294.  
  295.     if (ispipe)
  296.         ch_pipe();
  297.     else
  298.         ch_nonpipe();
  299.     (void) ch_nbuf(cbufs);
  300.     ch_flush();
  301.  
  302.     new_file = 1;
  303.  
  304.     if (every_first_cmd != NULL)
  305.         ungetsc(every_first_cmd);
  306.  
  307.     no_display = !any_display;
  308.     flush();
  309.     any_display = 1;
  310.  
  311.     if (is_tty)
  312.     {
  313.         /*
  314.          * Output is to a real tty.
  315.          */
  316.  
  317.         /*
  318.          * Indicate there is nothing displayed yet.
  319.          */
  320.         pos_clear();
  321.         clr_linenum();
  322.         if (no_display && errmsgs > 0)
  323.         {
  324.             /*
  325.              * We displayed some messages on error output
  326.              * (file descriptor 2; see error() function).
  327.              * Before erasing the screen contents,
  328.              * display the file name and wait for a keystroke.
  329.              */
  330.             parg.p_string = filename;
  331.             error("%s", &parg);
  332.         }
  333.     }
  334.     return (0);
  335. }
  336.  
  337. /*
  338.  * Edit a space-separated list of files.
  339.  * For each filename in the list, enter it into the ifile list.
  340.  * Then edit the first one.
  341.  */
  342.     public int
  343. edit_list(filelist)
  344.     char *filelist;
  345. {
  346.     IFILE save_curr_ifile;
  347.     char *good_filename;
  348.     char *filename;
  349.     char *gfilelist;
  350.     char *gfilename;
  351.     struct textlist tl_files;
  352.     struct textlist tl_gfiles;
  353.  
  354.     save_curr_ifile = curr_ifile;
  355.     good_filename = NULL;
  356.     
  357.     /*
  358.      * Run thru each filename in the list.
  359.      * Try to glob the filename.  
  360.      * If it doesn't expand, just try to open the filename.
  361.      * If it does expand, try to open each name in that list.
  362.      */
  363.     init_textlist(&tl_files, filelist);
  364.     filename = NULL;
  365.     while ((filename = forw_textlist(&tl_files, filename)) != NULL)
  366.     {
  367.         gfilelist = glob(filename);
  368.         if (gfilelist == NULL)
  369.         {
  370.             if (edit(filename) == 0 && good_filename == NULL)
  371.                 good_filename = get_filename(curr_ifile);
  372.         } else
  373.         {
  374.             init_textlist(&tl_gfiles, gfilelist);
  375.             gfilename = NULL;
  376.             while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
  377.             {
  378.                 if (edit(gfilename) == 0 && good_filename == NULL)
  379.                     good_filename = get_filename(curr_ifile);
  380.             }
  381.             free(gfilelist);
  382.         }
  383.     }
  384.     /*
  385.      * Edit the first valid filename in the list.
  386.      */
  387.     if (good_filename == NULL)
  388.         return (1);
  389.     if (get_ifile(good_filename, curr_ifile) == curr_ifile)
  390.         /*
  391.          * Trying to edit the current file; don't reopen it.
  392.          */
  393.         return (0);
  394.     if (edit_ifile(save_curr_ifile))
  395.         quit(-1);
  396.     return (edit(good_filename));
  397. }
  398.  
  399. /*
  400.  * Edit the first file in the command line (ifile) list.
  401.  */
  402.     public int
  403. edit_first()
  404. {
  405.     curr_ifile = NULL_IFILE;
  406.     return (edit_next(1));
  407. }
  408.  
  409. /*
  410.  * Edit the last file in the command line (ifile) list.
  411.  */
  412.     public int
  413. edit_last()
  414. {
  415.     curr_ifile = NULL_IFILE;
  416.     return (edit_prev(1));
  417. }
  418.  
  419.  
  420. /*
  421.  * Edit the next file in the command line (ifile) list.
  422.  */
  423.     public int
  424. edit_next(n)
  425.     int n;
  426. {
  427.     IFILE h;
  428.     IFILE next;
  429.  
  430.     h = curr_ifile;
  431.     /*
  432.      * Skip n filenames, then try to edit each filename.
  433.      */
  434.     for (;;)
  435.     {
  436.         next = next_ifile(h);
  437.         if (--n < 0)
  438.         {
  439.             if (edit_ifile(h) == 0)
  440.                 break;
  441.         }
  442.         if (next == NULL_IFILE)
  443.         {
  444.             /*
  445.              * Reached end of the ifile list.
  446.              */
  447.             return (1);
  448.         }
  449.         h = next;
  450.     } 
  451.     /*
  452.      * Found a file that we can edit.
  453.      */
  454.     return (0);
  455. }
  456.  
  457. /*
  458.  * Edit the previous file in the command line list.
  459.  */
  460.     public int
  461. edit_prev(n)
  462.     int n;
  463. {
  464.     IFILE h;
  465.     IFILE next;
  466.  
  467.     h = curr_ifile;
  468.     /*
  469.      * Skip n filenames, then try to edit each filename.
  470.      */
  471.     for (;;)
  472.     {
  473.         next = prev_ifile(h);
  474.         if (--n < 0)
  475.         {
  476.             if (edit_ifile(h) == 0)
  477.                 break;
  478.         }
  479.         if (next == NULL_IFILE)
  480.         {
  481.             /*
  482.              * Reached beginning of the ifile list.
  483.              */
  484.             return (1);
  485.         }
  486.         h = next;
  487.     } 
  488.     /*
  489.      * Found a file that we can edit.
  490.      */
  491.     return (0);
  492. }
  493.  
  494. /*
  495.  * Edit a specific file in the command line (ifile) list.
  496.  */
  497.     public int
  498. edit_index(n)
  499.     int n;
  500. {
  501.     IFILE h;
  502.  
  503.     h = NULL_IFILE;
  504.     do
  505.     {
  506.         if ((h = next_ifile(h)) == NULL_IFILE)
  507.         {
  508.             /*
  509.              * Reached end of the list without finding it.
  510.              */
  511.             return (1);
  512.         }
  513.     } while (get_index(h) != n);
  514.  
  515.     return (edit_ifile(h));
  516. }
  517.  
  518.     public int
  519. edit_stdin()
  520. {
  521.     if (isatty(fd0))
  522.     {
  523. #if MSOFTC
  524.         error("Missing filename (\"less -?\" for help)", NULL_PARG);
  525. #else
  526.         error("Missing filename (\"less -\\?\" for help)", NULL_PARG);
  527. #endif
  528.         quit(0);
  529.     }
  530.     return (edit("-"));
  531. }
  532.  
  533. /*
  534.  * Copy a file directly to standard output.
  535.  * Used if standard output is not a tty.
  536.  */
  537.     public void
  538. cat_file()
  539. {
  540.     register int c;
  541.  
  542.     while ((c = ch_forw_get()) != EOI)
  543.         putchr(c);
  544.     flush();
  545. }
  546.  
  547. #if LOGFILE
  548.  
  549. /*
  550.  * If the user asked for a log file and our input file
  551.  * is standard input, create the log file.  
  552.  * We take care not to blindly overwrite an existing file.
  553.  */
  554.     public void
  555. use_logfile(filename)
  556.     char *filename;
  557. {
  558.     register int exists;
  559.     register int answer;
  560.     PARG parg;
  561.  
  562.     /*
  563.      * {{ We could use access() here. }}
  564.      */
  565.     exists = open(filename, 0);
  566.     close(exists);
  567.     exists = (exists >= 0);
  568.  
  569.     /*
  570.      * Decide whether to overwrite the log file or append to it.
  571.      * (If it doesn't exist we "overwrite" it.
  572.      */
  573.     if (!exists || force_logfile)
  574.     {
  575.         /*
  576.          * Overwrite (or create) the log file.
  577.          */
  578.         answer = 'O';
  579.     } else
  580.     {
  581.         /*
  582.          * Ask user what to do.
  583.          */
  584.         parg.p_string = filename;
  585.         answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
  586.     }
  587.  
  588. loop:
  589.     switch (answer)
  590.     {
  591.     case 'O': case 'o':
  592.         /*
  593.          * Overwrite: create the file.
  594.          */
  595.         logfile = creat(filename, 0644);
  596.         break;
  597.     case 'A': case 'a':
  598.         /*
  599.          * Append: open the file and seek to the end.
  600.          */
  601. #if MSOFTC
  602.         logfile = open(filename, O_APPEND|O_WRONLY);
  603. #else
  604.         logfile = open(filename, 1);
  605. #endif
  606.         if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
  607.         {
  608.             close(logfile);
  609.             logfile = -1;
  610.         }
  611.         break;
  612.     case 'D': case 'd':
  613.         /*
  614.          * Don't do anything.
  615.          */
  616.         return;
  617.     case 'q':
  618.         quit(0);
  619.         /*NOTREACHED*/
  620.     default:
  621.         /*
  622.          * Eh?
  623.          */
  624.         answer = query("Overwrite, Append, or Don't log? ", NULL_PARG);
  625.         goto loop;
  626.     }
  627.  
  628.     if (logfile < 0)
  629.     {
  630.         /*
  631.          * Error in opening logfile.
  632.          */
  633.         parg.p_string = filename;
  634.         error("Cannot write to \"%s\"", &parg);
  635.     }
  636. }
  637.  
  638. #endif
  639.